/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 2 -*- */
/**
* @file usbserial.cpp
* 
* Library for easy comunication through USB ( Serial ).
* 
* @author Miguel Ángel Vílchez López 2009 <sacra@openmatica.es>
*
* Codigo is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* Codigo is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along
* with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "usbserial.hpp"
#include "debug.cpp"

CMUsbSerial::CMUsbSerial(const char* port="/dev/ttyUSB0",int baud=115200,bool debug=false,const char* filename=""):debug(debug,filename)
{
    this->port = (char*)malloc((strlen(port)+1) * sizeof(char*));
    strcpy(this->port,port);
    this->baud=baud;
    this->debug.debug("Trying to create the shared memory segment...");
    this->init();
    //if((this->id = shmget((key_t)this->id,this->buffer_size, IPC_CREAT | 0666))<0)
    //{ this->debug.error(strerror(errno),__FILE__,__FUNCTION__,(__LINE__)); }
}

CMUsbSerial::~CMUsbSerial()
{
    free(this->port);
}

int CMUsbSerial::init()
{
    struct termios toptions;

    this->fd = open(this->port, O_RDWR | O_NOCTTY | O_NDELAY);
    if (this->fd == -1)  {
            perror("init_serialport: Unable to open port ");
            return -1;
    }

    if (tcgetattr(this->fd, &toptions) < 0) {
            perror("init_serialport: Couldn't get term attributes");
            return -1;
    }
    speed_t brate = this->baud; // let you override switch below if needed
    switch(this->baud) {
    case 4800:   brate=B4800;   break;
    case 9600:   brate=B9600;   break;
    #ifdef B14400
    case 14400:  brate=B14400;  break;
    #endif
    case 19200:  brate=B19200;  break;
    #ifdef B28800
    case 28800:  brate=B28800;  break;
    #endif
    case 38400:  brate=B38400;  break;
    case 57600:  brate=B57600;  break;
    case 115200: brate=B115200; break;
    }
    cfsetispeed(&toptions, brate);
    cfsetospeed(&toptions, brate);

    // 8N1
    toptions.c_cflag &= ~PARENB;
    toptions.c_cflag &= ~CSTOPB;
    toptions.c_cflag &= ~CSIZE;
    toptions.c_cflag |= CS8;
    // no flow control
    toptions.c_cflag &= ~CRTSCTS;

    toptions.c_cflag |= CREAD | CLOCAL;  // turn on READ & ignore ctrl lines
    toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl

    toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
    toptions.c_oflag &= ~OPOST; // make raw

    // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
    toptions.c_cc[VMIN]  = 0;
    toptions.c_cc[VTIME] = 20;

    if( tcsetattr(this->fd, TCSANOW, &toptions) < 0) {
        perror("init_serialport: Couldn't set term attributes");
        return -1;
    }

}

int CMUsbSerial::read_until(char* data,char end)
{
    char b[1];
    int i=0;
    do {
        int n = read(this->fd, b, 1);  // read a char at a time
        if( n==-1) return -1;    // couldn't read
        if( n==0 ) {
            usleep( 10 * 1000 ); // wait 10 msec try again
            continue;
        }
        data[i] = b[0]; i++;
    } while( b[0] != end );

    data[i-1] = '\0';  // null terminate the string
    return 0;
}

int CMUsbSerial::read_bytes(char* data,int nbytes)
{
    char b[1];
    int i=0;
    do {
        int n = read(this->fd, b, 1);  // read a char at a time
        if( n==-1) return -1;    // couldn't read
        if( n==0 ) {
            usleep( 10 * 1000 ); // wait 10 msec try again
            continue;
        }
        data[i] = b[0]; i++;
    } while( i < nbytes );

    data[i] = '\0';  // null terminate the string
    return 0;
}

int CMUsbSerial::read_fromto(char* data,char begin,char end)
{
    bool start=false;
    char b[1];
    int i=0;
    do {
        int n = read(this->fd, b, 1);  // read a char at a time
        if( n==-1) return -1;    // couldn't read
        if( n==0 ) {
            usleep( 10 * 1000 ); // wait 10 msec try again
            continue;
        }
        if( start == true)
        {
            data[i] = b[0]; i++;
        }
        if( b[0] == begin) start=true;
    } while( b[0] != end );

    data[i-1] = '\0';  // null terminate the string
    return 0;
}

int CMUsbSerial::write_bytes(char* data)
{
    int len = strlen(data);
    int n = write(this->fd, data, len);
    if( n!=len )
        return -1;
    return 0;
}
